﻿namespace Manhua2.Provider
{
    using HtmlAgilityPack;
    using Manhua2.Common;
    using Manhua2.Helper;
    using Manhua2.SQLiteTable;
    using SharpCompress.Compressor;
    using SharpCompress.Compressor.Deflate;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading.Tasks;
    using Windows.Web.Syndication;

    public class Acg178Provider : IComicProvider
    {
        private static DateTime lastUpdatedTime = DateTime.MinValue;

        #region 初始化URL
        private string _rssUrl = "http://manhua.178.com/rss.xml";

        // 漫画首页
        private string _chUrl = "http://manhua.178.com/{0}/{1}.shtml";

        // 实际漫画页面
        private string _actualUrl = "http://imgfast.manhua.178.com/";

        // 搜索页面
        // private string _searchUrl = "http://s.acg.178.com/comicsum/search.php?s={0}";

        // 漫画目录页
        private string _manhuaDirUrl = "http://manhua.178.com/{0}/";
        #endregion

        private string provider = "acg178";

        public string ProviderName
        {
            get { return provider; }
        }

        public int TopCount { get { return 40; } }

        public async Task SaveLatestComicsAsync(bool cache)
        {
            if (cache)
            {
                if ((DateTime.Now - lastUpdatedTime).TotalMinutes < 10)
                {
                    return;
                }
            }

            lastUpdatedTime = DateTime.Now;

            using (DbContext context = new DbContext())
            {
                SyndicationClient client = new SyndicationClient();
                SyndicationFeed feed = await client.RetrieveFeedAsync(new Uri(this._rssUrl));
                foreach (var item in feed.Items)
                {
                    // 读取漫画信息
                    // 漫画标识
                    string token = item.Links[0].NodeValue;
                    token = token.Remove(token.LastIndexOf('/'));
                    token = token.Substring(token.LastIndexOf('/') + 1);

                    HtmlDocument html = new HtmlDocument();
                    html.LoadHtml(item.Summary.Text);

                    var comic = await this.GetComicAsync(context, this.provider, token);
                    if (comic == null)
                    {
                        comic = new SQLiteTable.SQLiteComic()
                        {
                            Author = item.Authors[0].NodeValue,
                            ChapterUrl = item.Links[0].NodeValue,
                            CoverUrl = html.DocumentNode.LastChild.Element("img").GetAttributeValue("src", ""),
                            Provider = this.provider,
                            RssUrl = string.Format("http://manhua.178.com/m/{0}/rss.xml", token),
                            Title = item.Title.Text,
                            Token = token,
                        };

                        await context.InsertAsync(comic);
                    }

                    // 读取最新章节信息
                    // 章节标识
                    token = html.DocumentNode.Element("a").GetAttributeValue("href", "");
                    token = token.Remove(token.LastIndexOf('?'))
                                 .Remove(token.LastIndexOf('.'));
                    token = token.Substring(token.LastIndexOf('/') + 1);

                    var chapter = await this.GetChapterAsync(context, comic.Id, token);

                    if (chapter == null)
                    {
                        chapter = new SQLiteTable.SQLiteChapter()
                        {
                            ComicId = comic.Id,
                            Title = html.DocumentNode.Element("a").InnerText,
                            Token = token,
                            Updated = item.PublishedDate.LocalDateTime,
                            Url = string.Format(this._chUrl, comic.Token, token),
                        };

                        await context.InsertAsync(chapter);
                    }
                    else
                    {
                        chapter.Title = html.DocumentNode.Element("a").InnerText;
                        chapter.Url = string.Format(this._chUrl, comic.Token, token);
                        chapter.Updated = item.PublishedDate.LocalDateTime;

                        await context.UpdateAsync(chapter);
                    }
                }
            }
        }

        public async Task SavePagesAsync(int chapterId)
        {
            using (DbContext context = new DbContext())
            {
                var chapter = await this.GetChapterAsync(context, chapterId);
                if (chapter == null)
                    throw new Exception("章节不存在");

                HtmlDocument html = new HtmlDocument();
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(chapter.Url);
                using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
                using (Stream stream = response.GetResponseStream())
                using (MemoryStream ms = new MemoryStream())
                {
                    try
                    {
                        byte[] buffer = new byte[4096];
                        int len;
                        while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            ms.Write(buffer, 0, len);
                        }

                        ms.Seek(0, SeekOrigin.Begin);
                        GZipStream zip = new GZipStream(
                                ms,
                                CompressionMode.Decompress);
                        html.Load(zip);
                    }
                    catch
                    {
                        ms.Seek(0, SeekOrigin.Begin);
                        html.Load(ms);
                    }

                    HtmlNode script = html.DocumentNode.Element("html").Element("head").Element("script");
                    if (script == null ||
                       string.IsNullOrEmpty(script.InnerHtml))
                        throw new ArgumentException("加载漫画失败...");
                    Match match = Regex.Match(script.InnerHtml, "(?<=var g_chapter_name = \").+(?=\")");
                    if (!match.Success)
                        throw new ArgumentException("加载漫画失败...");

                    chapter.Title = match.Value;

                    var output = JavaScriptHelper.Decode(script.InnerHtml);

                    // 删除该漫画所有图片， 确保不会出现重复数据造成错误。
                    await context.ClearPages(chapterId);

                    string input = output.Substring(output.IndexOf('[') + 1);
                    input = input.Remove(input.LastIndexOf(']'));
                    string[] split = input.Split(',');
                    int i = 1;
                    foreach (var v in split)
                    {
                        SQLiteTable.SQLitePage page = new SQLiteTable.SQLitePage();
                        page.ChapterId = chapterId;
                        page.Page = i++;
                        page.Url = this._actualUrl + ConvertString(v).Trim('"');

                        await context.InsertAsync(page);
                    }

                    // 修改总页数
                    chapter.TotalPage = split.Length;
                    await context.UpdateAsync(chapter);
                }
            }
        }

        public async Task SaveChaptersAsync(int comicId)
        {
            using (DbContext context = new DbContext())
            {
                var comic = await context.GetAsync<SQLiteComic>(comicId);
                if (comic == null) throw new ArgumentNullException("comic");


                // 从网络获取数据
                HtmlDocument html = new HtmlDocument();
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
                    string.Format(this._manhuaDirUrl, comic.Token));
                using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
                using (Stream stream = response.GetResponseStream())
                using (MemoryStream ms = new MemoryStream())
                {
                    try
                    {
                        byte[] buffer = new byte[4096];
                        int len;
                        while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            ms.Write(buffer, 0, len);
                        }

                        ms.Seek(0, SeekOrigin.Begin);
                        GZipStream zip = new GZipStream(
                                ms,
                                CompressionMode.Decompress);
                        html.Load(zip);
                    }
                    catch
                    {
                        ms.Seek(0, SeekOrigin.Begin);
                        html.Load(ms);
                    }
                }

                HtmlNode body = html.DocumentNode.Element("html")
                                   .Element("body");
                                
                List<HtmlNode> search = GetElementByClass(body, "cartoon_online_border");
                if (search == null ||
                    search.Count == 0) return;
                                
                foreach (HtmlNode node in search)
                {
                    foreach (HtmlNode item in node.Element("ul").Elements("li"))
                    {
                        HtmlNode a = item.Element("a");

                        string url = a.GetAttributeValue("href", string.Empty);
                        if (!url.StartsWith("http"))
                        {
                            url = "http://manhua.178.com/" + url;
                        }

                        string token = url.Substring(url.LastIndexOf('/') + 1);
                        token = token.Remove(token.LastIndexOf('.'));

                        var chapter = await this.GetChapterAsync(context, comic.Id, token);
                        if (chapter == null)
                        {
                            chapter = new SQLiteChapter();
                            chapter.ComicId = comic.Id;
                            chapter.Title = a.InnerText;
                            chapter.Url = url;
                            chapter.Token = token;
                            chapter.Updated = new DateTime(2000, 1, 1);
                            await context.InsertAsync(chapter);
                        }
                        else
                        {
                            chapter.Title = a.InnerText;
                            chapter.Url = url;

                            await context.UpdateAsync(chapter);
                        }
                    }
                }
            }
        }
        
        public override string ToString()
        {
            return this.provider;
        }

        #region Methods
        private string ConvertString(string input)
        {
            input = input.Replace(@"\\/", @"/");
            StringBuilder sb = new StringBuilder();
            string[] split = Regex.Split(input, @"\\u");
            sb.Append(split[0]);
            for (int i = 1; i < split.Length; i++)
            {
                string code = split[i];
                sb.Append(char.ConvertFromUtf32(int.Parse(code.Substring(0, 4), System.Globalization.NumberStyles.HexNumber)));
                sb.Append(code.Substring(4));
            }

            return sb.Replace(@"\", "").ToString();
        }

        private List<HtmlNode> GetElementByClass(HtmlNode body, string name)
        {
            List<HtmlNode> results = new List<HtmlNode>();
            foreach (HtmlNode s in body.ChildNodes)
            {
                if (s.GetAttributeValue("class", string.Empty) == name)
                {
                    results.Add(s);
                    continue;
                }

                if (s.ChildNodes.Count != 0)
                {
                    var nodes = GetElementByClass(s, name);
                    results.AddRange(nodes);
                }
            }

            return results;
        }

        private Task<SQLiteChapter> GetChapterAsync(DbContext context, int id)
        {
            return context.GetAsync<SQLiteChapter>(id);
        }

        public Task<SQLiteChapter> GetChapterAsync(DbContext context, int comicId, string token)
        {
            return context.FirstOrDefault<SQLiteChapter>(
                        "SELECT * FROM chapters WHERE comic_id = ? AND token = ?",
                        comicId,
                        token);
        }

        public Task<SQLiteComic> GetComicAsync(DbContext context, string provider, string token)
        {
            return context.FirstOrDefault<SQLiteComic>(
                "SELECT * FROM comics WHERE provider = ? AND token = ?",
                provider,
                token);
        }
        #endregion
    }
}
